/***************************************************************************************
 The copyright in this software is being made available under the BSD License, included
 below. This software may be subject to other third party rights,including patent rights,
 and no such rights are granted under this license.

 Regents of the Tsinghua University, Peking University, Zhejiang University, HKUST
 Tsinghua University, Peking University, Zhejiang University, HKUST 2011

 Copyright (c) 2011, Regents of the Tsinghua University, Peking University, Zhejiang
 University, HKUST. All Rights Reserved.

 Redistribution and use in source and binary forms, with or without modification, are
 permitted provided that the following conditions are met:
   * Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.
   * Neither the names of the copyright holders nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 OF SUCH DAMAGE.
***************************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "contributors.h"
#include "qcoder.h"
prob_state_t eq_prob_fsm[] =
{
    /* 0 */ { 0x5555, 0, 0, 0 }
};
prob_state_t standard_prob_fsm[] =
{
    /* state lpsInterval nextStateLps nextStateMps doSwitchMps */
    /* 0 */ { 0x5a1d, 1, 1, 1 },
    /* 1 */ { 0x2586, 14, 2, 0 },
    /* 2 */ { 0x1114, 16, 3, 0 },
    /* 3 */ { 0x080b, 18, 4, 0 },
    /* 4 */ { 0x03d8, 20, 5, 0 },
    /* 5 */ { 0x01da, 23, 6, 0 },
    /* 6 */ { 0x00e5, 25, 7, 0 },
    /* 7 */ { 0x006f, 28, 8, 0 },
    /* 8 */ { 0x0036, 30, 9, 0 },
    /* 9 */ { 0x001a, 33, 10, 0 },
    /* 10 */ { 0x000d, 35, 11, 0 },
    /* 11 */ { 0x0006, 9, 12, 0 },
    /* 12 */ { 0x0003, 10, 13, 0 },
    /* 13 */ { 0x0001, 12, 13, 0 },
    /* 14 */ { 0x5a7f, 15, 15, 1 },
    /* 15 */ { 0x3f25, 36, 16, 0 },
    /* 16 */ { 0x2cf2, 38, 17, 0 },
    /* 17 */ { 0x207c, 39, 18, 0 },
    /* 18 */ { 0x17b9, 40, 19, 0 },
    /* 19 */ { 0x1182, 42, 20, 0 },
    /* 20 */ { 0x0cef, 43, 21, 0 },
    /* 21 */ { 0x09a1, 45, 22, 0 },
    /* 22 */ { 0x072f, 46, 23, 0 },
    /* 23 */ { 0x055c, 48, 24, 0 },
    /* 24 */ { 0x0406, 49, 25, 0 },
    /* 25 */ { 0x0303, 51, 26, 0 },
    /* 26 */ { 0x0240, 52, 27, 0 },
    /* 27 */ { 0x01b1, 54, 28, 0 },
    /* 28 */ { 0x0144, 56, 29, 0 },
    /* 29 */ { 0x00f5, 57, 30, 0 },
    /* 30 */ { 0x00b7, 59, 31, 0 },
    /* 31 */ { 0x008a, 60, 32, 0 },
    /* 32 */ { 0x0068, 62, 33, 0 },
    /* 33 */ { 0x004e, 63, 34, 0 },
    /* 34 */ { 0x003b, 32, 35, 0 },
    /* 35 */ { 0x002c, 33, 9, 0 },
    /* 36 */ { 0x5ae1, 37, 37, 1 },
    /* 37 */ { 0x484c, 64, 38, 0 },
    /* 38 */ { 0x3a0d, 65, 39, 0 },
    /* 39 */ { 0x2ef1, 67, 40, 0 },
    /* 40 */ { 0x261f, 68, 41, 0 },
    /* 41 */ { 0x1f33, 69, 42, 0 },
    /* 42 */ { 0x19a8, 70, 43, 0 },
    /* 43 */ { 0x1518, 72, 44, 0 },
    /* 44 */ { 0x1177, 73, 45, 0 },
    /* 45 */ { 0x0e74, 74, 46, 0 },
    /* 46 */ { 0x0bfb, 75, 47, 0 },
    /* 47 */ { 0x09f8, 77, 48, 0 },
    /* 48 */ { 0x0861, 78, 49, 0 },
    /* 49 */ { 0x0706, 79, 50, 0 },
    /* 50 */ { 0x05cd, 48, 51, 0 },
    /* 51 */ { 0x04de, 50, 52, 0 },
    /* 52 */ { 0x040f, 50, 53, 0 },
    /* 53 */ { 0x0363, 51, 54, 0 },
    /* 54 */ { 0x02d4, 52, 55, 0 },
    /* 55 */ { 0x025c, 53, 56, 0 },
    /* 56 */ { 0x01f8, 54, 57, 0 },
    /* 57 */ { 0x01a4, 55, 58, 0 },
    /* 58 */ { 0x0160, 56, 59, 0 },
    /* 59 */ { 0x0125, 57, 60, 0 },
    /* 60 */ { 0x00f6, 58, 61, 0 },
    /* 61 */ { 0x00cb, 59, 62, 0 },
    /* 62 */ { 0x00ab, 61, 63, 0 },
    /* 63 */ { 0x008f, 61, 32, 0 },
    /* 64 */ { 0x5b12, 65, 65, 1 },
    /* 65 */ { 0x4d04, 80, 66, 0 },
    /* 66 */ { 0x412c, 81, 67, 0 },
    /* 67 */ { 0x37d8, 82, 68, 0 },
    /* 68 */ { 0x2fe8, 83, 69, 0 },
    /* 69 */ { 0x293c, 84, 70, 0 },
    /* 70 */ { 0x2379, 86, 71, 0 },
    /* 71 */ { 0x1edf, 87, 72, 0 },
    /* 72 */ { 0x1aa9, 87, 73, 0 },
    /* 73 */ { 0x174e, 72, 74, 0 },
    /* 74 */ { 0x1424, 72, 75, 0 },
    /* 75 */ { 0x119c, 74, 76, 0 },
    /* 76 */ { 0x0f6b, 74, 77, 0 },
    /* 77 */ { 0x0d51, 75, 78, 0 },
    /* 78 */ { 0x0bb6, 77, 79, 0 },
    /* 79 */ { 0x0a40, 77, 48, 0 },
    /* 80 */ { 0x5832, 80, 81, 1 },
    /* 81 */ { 0x4d1c, 88, 82, 0 },
    /* 82 */ { 0x438e, 89, 83, 0 },
    /* 83 */ { 0x3bdd, 90, 84, 0 },
    /* 84 */ { 0x34ee, 91, 85, 0 },
    /* 85 */ { 0x2eae, 92, 86, 0 },
    /* 86 */ { 0x299a, 93, 87, 0 },
    /* 87 */ { 0x2516, 86, 71, 0 },
    /* 88 */ { 0x5570, 88, 89, 1 },
    /* 89 */ { 0x4ca9, 95, 90, 0 },
    /* 90 */ { 0x44d9, 96, 91, 0 },
    /* 91 */ { 0x3e22, 97, 92, 0 },
    /* 92 */ { 0x3824, 99, 93, 0 },
    /* 93 */ { 0x32b4, 99, 94, 0 },
    /* 94 */ { 0x2e17, 93, 86, 0 },
    /* 95 */ { 0x56a8, 95, 96, 1 },
    /* 96 */ { 0x4f46, 101, 97, 0 },
    /* 97 */ { 0x47e5, 102, 98, 0 },
    /* 98 */ { 0x41cf, 103, 99, 0 },
    /* 99 */ { 0x3c3d, 104, 100, 0 },
    /* 100 */ { 0x375e, 99, 93, 0 },
    /* 101 */ { 0x5231, 105, 102, 0 },
    /* 102 */ { 0x4c0f, 106, 103, 0 },
    /* 103 */ { 0x4639, 107, 104, 0 },
    /* 104 */ { 0x415e, 103, 99, 0 },
    /* 105 */ { 0x5627, 105, 106, 1 },
    /* 106 */ { 0x50e7, 108, 107, 0 },
    /* 107 */ { 0x4b85, 109, 103, 0 },
    /* 108 */ { 0x5597, 110, 109, 0 },
    /* 109 */ { 0x504f, 111, 107, 0 },
    /* 110 */ { 0x5a10, 110, 111, 1 },
    /* 111 */ { 0x5522, 112, 109, 0 },
    /* 112 */ { 0x59eb, 112, 111, 1 }
};

void output_byte( bitstream_t* currStream, short buffer )
{
    currStream->stream_buffer[currStream->byte_pos++] = buffer;
    currStream->bits_to_go = 8;
    return;
}

void put_byte( qcoder_env_t* qcoder, short buffer )
{
    if ( qcoder->is_pending_byte )
    {
        if ( qcoder->pending_byte == 0 )
        {
            qcoder->num_stacked_zeros += 1;
        }
        else
        {
            while ( qcoder->num_stacked_zeros > 0 )
            {
                output_byte( curr_stream, 0 );
                qcoder->num_stacked_zeros -= 1;
            }
            output_byte( curr_stream, qcoder->pending_byte );
        }
    }
    qcoder->pending_byte = buffer;
    qcoder->is_pending_byte = 1;
}

void qcoder_init( qcoder_env_t* qcoder, bitstream_t* currStream )
{
    int i, j;

    qcoder->interval = 0x10000;
    qcoder->code = 0;
    qcoder->code_bits= 11;
    qcoder->pending_byte= 0;
    qcoder->is_pending_byte = 0;
    qcoder->num_stacked_ffs = 0;
    qcoder->num_stacked_zeros = 0;
    qcoder->first_byte = 1;

    qcoder->eq_prob_context.mps = qcoder->eq_prob_context.state = 0;
    qcoder->eq_prob_context.prob_fsm = &( eq_prob_fsm[0] );

    qcoder->cx_eob_flag.mps = qcoder->cx_eob_flag.state = 0;
    qcoder->cx_eob_flag.prob_fsm = standard_prob_fsm;

    for( i=0; i<NUM_COEFF_B8_CX_QCODER_R_MAX; i++ )
    {
        for( j=0; j<NUM_COEFF_B8_CX_QCODER_C_MAX; j++ )
        {
            qcoder->cx_luma_8x8[i][j].mps = qcoder->cx_luma_8x8[i][j].state = 0;
            qcoder->cx_luma_8x8[i][j].prob_fsm = standard_prob_fsm;

            qcoder->cx_chroma_8x8[i][j].mps = qcoder->cx_chroma_8x8[i][j].state = 0;
            qcoder->cx_chroma_8x8[i][j].prob_fsm = standard_prob_fsm;
        }
    }

    for( i=0; i<NUM_COEFF_B16_CX_QCODER_R_MAX; i++ )
    {
        for( j=0; j<NUM_COEFF_B16_CX_QCODER_C_MAX; j++ )
        {
            qcoder->cx_luma_16x16[i][j].mps = qcoder->cx_luma_16x16[i][j].state = 0;
            qcoder->cx_luma_16x16[i][j].prob_fsm = standard_prob_fsm;
        }
    }

    for( i=0; i<NUM_COEFF_B4_CX_QCODER_R_MAX; i++ )
    {
        for( j=0; j<NUM_COEFF_B4_CX_QCODER_C_MAX; j++ )
        {
            qcoder->cx_luma_4x4[i][j].mps = qcoder->cx_luma_4x4[i][j].state = 0;
            qcoder->cx_luma_4x4[i][j].prob_fsm = standard_prob_fsm;
        }
    }

    for( i=0; i<NUM_CBP_CX_QCODER_INTRA; i++ )
    {
        qcoder->cx_intra_cbp[i].mps = qcoder->cx_intra_cbp[i].state = 0;
        qcoder->cx_intra_cbp[i].prob_fsm = standard_prob_fsm;
    }
    for( i=0; i<NUM_CBP_CX_QCODER_INTER; i++ )
    {
        qcoder->cx_inter_cbp[i].mps = qcoder->cx_inter_cbp[i].state = 0;
        qcoder->cx_inter_cbp[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_DELTAQP_CX_QCODER; i++ )
    {
        qcoder->cx_delta_qp[i].mps = qcoder->cx_delta_qp[i].state = 0;
        qcoder->cx_delta_qp[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_INTRAMODE_CX_QCODER; i++ )
    {
        qcoder->cx_intra_luma_mode[i].mps = qcoder->cx_intra_luma_mode[i].state = 0;
        qcoder->cx_intra_luma_mode[i].prob_fsm = standard_prob_fsm;

        qcoder->cx_intra_chroma_mode[i].mps = qcoder->cx_intra_chroma_mode[i].state = 0;
        qcoder->cx_intra_chroma_mode[i].prob_fsm = standard_prob_fsm;
    }

    qcoder->cx_inter_ref.mps = qcoder->cx_inter_ref.state = 0;
    qcoder->cx_inter_ref.prob_fsm = standard_prob_fsm;

    for( i=0; i<NUM_MVD_CX_QCODER_MAX; i++ )
    {
        qcoder->cx_mvd_x[i].mps = qcoder->cx_mvd_x[i].state = 0;
        qcoder->cx_mvd_x[i].prob_fsm = standard_prob_fsm;

        qcoder->cx_mvd_y[i].mps = qcoder->cx_mvd_y[i].state = 0;
        qcoder->cx_mvd_y[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_MBTYPE_CX_QCODER; i++ )
    {
        qcoder->cx_mbtype[i].mps = qcoder->cx_mbtype[i].state = 0;
        qcoder->cx_mbtype[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_BLOCKSIZE_CX_QCODER; i++ )
    {
        qcoder->cx_blocksize[i].mps = qcoder->cx_blocksize[i].state = 0;
        qcoder->cx_blocksize[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_SUBBLOCKSIZE_CX_QCODER; i++ )
    {
        qcoder->cx_subblocksize[i].mps = qcoder->cx_subblocksize[i].state = 0;
        qcoder->cx_subblocksize[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_PRED_DIRECTION_CX_QCODER; i++ )
    {
        qcoder->cx_pred_direction[i].mps = qcoder->cx_pred_direction[i].state = 0;
        qcoder->cx_pred_direction[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_INTERSKIP_CX_QCODER; i++ )
    {
        qcoder->cx_inter_skip[i].mps = qcoder->cx_inter_skip[i].state = 0;
        qcoder->cx_inter_skip[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<NUM_PARTITION; i++ )
    {
        int j;
        for( j=0; j< NUM_DIRECTION; j++ )
        {
            qcoder->cx_mbsubtype_Direction[i][j].mps = qcoder->cx_mbsubtype_Direction[i][j].state = 0;
            qcoder->cx_mbsubtype_Direction[i][j].prob_fsm = standard_prob_fsm;
        }
    }

    for( i=0; i<NUM_MBSUBTYPE_CX_QCODER; i++ )
    {
        qcoder->cx_mbsubtype[i].mps = qcoder->cx_mbsubtype[i].state = 0;
        qcoder->cx_mbsubtype[i].prob_fsm = standard_prob_fsm;
    }

    qcoder->cx_transform_s16_flag.mps = qcoder->cx_transform_s16_flag.state = 0;
    qcoder->cx_transform_s16_flag.prob_fsm = standard_prob_fsm;

    qcoder->cx_transform_s4_flag.mps = qcoder->cx_transform_s4_flag.state = 0;
    qcoder->cx_transform_s4_flag.prob_fsm = standard_prob_fsm;

    for( i=0; i<64; i++ )
    {
        qcoder->cx_eob_flag_8x8[i].mps = 0;
        qcoder->cx_eob_flag_8x8[i].state = 0;
        qcoder->cx_eob_flag_8x8[i].prob_fsm = standard_prob_fsm;

        qcoder->cx_eob_flag_chroma[i].mps = 0;
        qcoder->cx_eob_flag_chroma[i].state = 0;
        qcoder->cx_eob_flag_chroma[i].prob_fsm = standard_prob_fsm;
    }

    for( i=0; i<16; i++ )
    {
        qcoder->cx_eob_flag_4x4[i].mps = 0;
        qcoder->cx_eob_flag_4x4[i].state = 0;
        qcoder->cx_eob_flag_4x4[i].prob_fsm = standard_prob_fsm;
    }

    return;
}

void qcoder_carry_propgate( qcoder_env_t* qcoder )
{
    unsigned long outBits = qcoder->code >> 19;
    qcoder->code &= 0x7FFFF;
    if ( outBits < 0xFF )
    {
        while ( qcoder->num_stacked_ffs != 0 )
        {
            put_byte( qcoder, 0xFF );
            put_byte( qcoder, 0x01 );
            qcoder->num_stacked_ffs -= 1;
        }
        put_byte( qcoder, outBits );
    }
    else if ( outBits > 0xFF )
    {
        if ( ++qcoder->pending_byte == 0xFF )
        {
            put_byte( qcoder, 0x01 );
            qcoder->nbits += 8;  //bit counter, only for rdo and stats [9/22/2011 Will]
        }
        while ( qcoder->num_stacked_ffs != 0 )
        {
            put_byte( qcoder, 0 );
            qcoder->num_stacked_ffs -= 1;
            qcoder->nbits -= 8;  //bit counter, only for rdo and stats [9/22/2011 Will]
        }
        put_byte( qcoder, outBits & 0xFF );
    }
    else
    {
        qcoder->num_stacked_ffs += 1;
        qcoder->nbits +=8;  //bit counter, only for rdo and stats [9/22/2011 Will]
    }
}

void qcoder_renorm( qcoder_env_t* qcoder )
{
    do
    {
        qcoder->interval <<= 1;
        qcoder->code <<= 1;
        qcoder->nbits ++;  //bit counter, only for rdo and stats [9/22/2011 Will]
        if ( --qcoder->code_bits == 0 )
        {
            qcoder_carry_propgate( qcoder );
            qcoder->code_bits= 8;
            qcoder->first_byte = 0;
        }
    }
    while ( qcoder->interval < 0x8000 );
}

void qcoder_encode_symbol( qcoder_env_t* qcoder, prob_context_t* contextp, int symbol )
{
    prob_state_t *probFsm = contextp->prob_fsm;
    qcoder->interval -= probFsm[contextp->state].lps_interval;
    if ( symbol == contextp->mps )
    {
        // code MPS
        if ( qcoder->interval < 0x8000 )
        {
            if ( qcoder->interval < ( unsigned long )probFsm[contextp->state].lps_interval )
            {
                qcoder->code += qcoder->interval;
                qcoder->interval = probFsm[contextp->state].lps_interval;
            }
            contextp->state = probFsm[contextp->state].next_state_mps;
            qcoder_renorm( qcoder );
        }
    }
    else
    {
        // code LPS
        if ( qcoder->interval >= ( unsigned long )probFsm[contextp->state].lps_interval )
        {
            qcoder->code += qcoder->interval;
            qcoder->interval = probFsm[contextp->state].lps_interval;
        }
        if ( probFsm[contextp->state].do_switch_mps )
        {
            contextp->mps = ! contextp->mps;
        }
        contextp->state = probFsm[contextp->state].next_state_lps;
        qcoder_renorm( qcoder );
    }
}

void qcoder_flush( qcoder_env_t* qcoder )
{
    int t = ( qcoder->code + qcoder->interval - 1 ) & 0xFFFF0000;
    if ( t < ( int )qcoder->code ) t += 0x8000;
    qcoder->code = t << qcoder->code_bits;
    qcoder_carry_propgate( qcoder );
    qcoder->code <<= 8;
    qcoder_carry_propgate( qcoder );
    put_byte( qcoder, 0 );
    qcoder->num_stacked_zeros = 0;
    // write end of stream flag
    output_byte( curr_stream, 0xFF );
    output_byte( curr_stream, 0x2 );
}

prob_context_t* select_context( qcoder_env_t* qcoder, int n, prob_context_t* contexts, int num_contexts )
{
    if( n< num_contexts )
        return &( contexts[n] );
    else
        return &( qcoder->eq_prob_context );
}

void aricod_encode_flag( qcoder_env_t* qcoder, prob_context_t* flag_context, int symbol )
{
    int n=0;
    qcoder_encode_symbol( qcoder, &( flag_context[n] ), symbol!=0 );
    return;
}



void aricod_encode_signed_fixed_bits( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int nBits, int value )
{
    int n = 0, i;
    prob_context_t* contextp = select_context( qcoder, n++, contexts, num_contexts );

    qcoder_encode_symbol( qcoder, contextp, value == 0 );
    if ( value == 0 ) return;
    contextp = select_context( qcoder, n++, contexts, num_contexts );
    qcoder_encode_symbol( qcoder, contextp, value < 0 );
    if ( value < 0 ) value = -value;
    value -= 1;
    for ( i = nBits - 1; i >= 0; i-- )
    {
        contextp = select_context( qcoder, n++, contexts, num_contexts );
        qcoder_encode_symbol( qcoder, contextp, ( ( value & ( 1 << i ) )!= 0 ) );
    }
}

void aricod_encode_unary( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int value )
{
    int n = 0; // context index
    while ( value-- > 0 )
    {
        qcoder_encode_symbol( qcoder, &( contexts[n] ), 1 );
        // advance to next context if sufficient contexts
        if ( n < num_contexts - 1 ) n++;
    }
    qcoder_encode_symbol( qcoder, &( contexts[n] ), 0 );
}

void aricod_encode_signed_unary( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int value )
{
    int n = 0; // context index
    // map signed value to unsigned value
    value = ( abs( value ) << 1 ) - ( value > 0 ) ? 1 : 0;
    while ( value-- > 0 )
    {
        qcoder_encode_symbol( qcoder, &( contexts[n] ), 1 );
        // advance to next context if sufficient contexts
        if ( n < num_contexts - 1 ) n++;
    }
    qcoder_encode_symbol( qcoder, &( contexts[n] ), 0 );
}

void aricod_encode_trunary( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int maxValue, int value )
{
    // PRECONDITION: value MUST be <= maxValue
    int n = 0;
    while ( value-- > 0 )
    {
        qcoder_encode_symbol( qcoder, &( contexts[n] ), 1 );
        maxValue -= 1;
        // advance to next context if sufficient contexts
        if ( n < num_contexts - 1 ) n++;
    }
    // Code terminating 0 if value < maxValue
    if ( maxValue > 0 )
    {
        qcoder_encode_symbol( qcoder, &( contexts[n] ), 0 );
    }
}


void aricod_encode_expGolomb( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int k, int value )
{
    int n = 0;

    qcoder_encode_symbol( qcoder, &( contexts[n] ), value == 0 );
    if ( value == 0 ) return;
    if ( n < num_contexts - 1 ) n++;
    value -= 1;
    while ( value >= ( 1 << k ) )
    {
        qcoder_encode_symbol( qcoder, &( contexts[n] ), 1 );
        value -= 1 << k++;
        if ( n < num_contexts - 1 ) n++;
    }

    // code zero to mark end of exponent
    qcoder_encode_symbol( qcoder, &( contexts[n] ), 0 );
    // advance to next context if sufficient contexts
    if ( n < num_contexts - 1 ) n++;

    while ( k-- > 0 )
    {
        qcoder_encode_symbol( qcoder, &( qcoder->eq_prob_context ), ( ( value & ( 1 << k ) ) != 0 ) );
        if ( n < num_contexts - 1 ) n++;
    }
}

void aricod_encode_signed_expGolomb( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int k, int value )
{
    int n = 0;

    qcoder_encode_symbol( qcoder, &( contexts[n] ), value == 0 );
    if ( value == 0 ) return;
    if ( n < num_contexts - 1 ) n++;
    qcoder_encode_symbol( qcoder, &( contexts[n] ), value < 0 );
    if ( value < 0 ) value = -value;
    if ( n < num_contexts - 1 ) n++;
    value -= 1;
    while ( value >= ( 1 << k ) )
    {
        qcoder_encode_symbol( qcoder, &( contexts[n] ), 1 );
        value -= 1 << k++;
        if ( n < num_contexts - 1 ) n++;
    }

    // code zero to mark end of exponent
    qcoder_encode_symbol( qcoder, &( contexts[n] ), 0 );
    // advance to next context if sufficient contexts
    if ( n < num_contexts - 1 ) n++;

    while ( k-- > 0 )
    {
        qcoder_encode_symbol( qcoder, &( qcoder->eq_prob_context ), ( ( value & ( 1 << k ) ) != 0 ) );
        if ( n < num_contexts - 1 ) n++;
    }
}

int get_current_rate()
{
    if( qcoder->first_byte )
        return ( curr_stream->byte_pos*8 - curr_stream->bits_to_go + 11 - qcoder->code_bits );
    else
        return ( curr_stream->byte_pos*8 - curr_stream->bits_to_go + 8 - qcoder->code_bits );
}


int qcoder_encode_delta_qp( qcoder_env_t* qcoder, int delta_qp )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    //aricod_encode_signed_unary(qcoder, &(qcoder->cx_delta_qp[0]), 4, 0, delta_qp);
    // too many parameters??
    aricod_encode_signed_unary( qcoder, &( qcoder->cx_delta_qp[0] ), NUM_DELTAQP_CX_QCODER, 0 );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_eobflag_8x8( qcoder_env_t* qcoder, int eobflag, int position )
{
    qcoder->nbits = 0;
    aricod_encode_flag( qcoder, &( qcoder->cx_eob_flag_8x8[position] ), eobflag );
    return qcoder->nbits;
}

int qcoder_encode_eobflag_4x4( qcoder_env_t* qcoder, int eobflag, int position )
{
    qcoder->nbits = 0;
    aricod_encode_flag( qcoder, &( qcoder->cx_eob_flag_4x4[position] ), eobflag );
    return qcoder->nbits;
}

int qcoder_encode_eobflag_chroma( qcoder_env_t* qcoder, int eobflag, int position )
{
    qcoder->nbits = 0;
    aricod_encode_flag( qcoder, &( qcoder->cx_eob_flag_chroma[position] ), eobflag );
    return qcoder->nbits;
}

int qcoder_encode_eobflag( qcoder_env_t* qcoder, int eobflag )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]

    aricod_encode_flag( qcoder, &( qcoder->cx_eob_flag ), eobflag );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}


int qcoder_encode_intra_luma_mode( qcoder_env_t* qcoder, int mode ) //Trunary [9/17/2011 Will]
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    //aricod_encode_expGolomb(qcoder, &(qcoder->cx_intra_luma_mode[0]), 3, 0, mode);
    aricod_encode_trunary( qcoder, &( qcoder->cx_intra_luma_mode[0] ), NUM_INTRAMODE_CX_QCODER, NUM_INTRAMODE_CX_QCODER, mode );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_intra_chroma_mode( qcoder_env_t* qcoder, int mode ) //Trunary [9/17/2011 Will]
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    //aricod_encode_expGolomb(qcoder, &(qcoder->cx_intra_chroma_mode[0]), 3, 0, mode);
    aricod_encode_trunary( qcoder, &( qcoder->cx_intra_chroma_mode[0] ), NUM_INTRAMODE_CX_QCODER, NUM_INTRAMODE_CX_QCODER, mode );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_inter_ref( qcoder_env_t* qcoder, int ref )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_flag( qcoder, &( qcoder->cx_inter_ref ), ref );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_mvd_x( qcoder_env_t* qcoder, int mvdx )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_mvd_x[0] ), NUM_MVD_CX_QCODER_MAX, 0, mvdx );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_mvd_y( qcoder_env_t* qcoder, int mvdy )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_mvd_y[0] ), NUM_MVD_CX_QCODER_MAX, 0, mvdy );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_mvd_x_num( qcoder_env_t* qcoder, int mvdx, int cn )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_mvd_x[0] ), cn, 0, mvdx );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_mvd_y_num( qcoder_env_t* qcoder, int mvdy, int cn )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_mvd_y[0] ), cn, 0, mvdy );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_inter_skip( qcoder_env_t* qcoder, int skip )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    //aricod_encode_fixed_bits(qcoder, &(qcoder->cx_inter_skip[0]), NUM_MBSUBTYPE_CX_QCODER, 1, skip);
    aricod_encode_fixed_bits( qcoder, &( qcoder->cx_inter_skip[0] ), NUM_INTERSKIP_CX_QCODER, 1, skip ); //xuyan0930

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}


int qcoder_encode_blocksize( qcoder_env_t* qcoder, int value )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    //aricod_encode_fixed_bits(qcoder, &(qcoder->cx_blocksize[0]), NUM_BLOCKSIZE_CX_QCODER, 1, value);//xuyan0930
    aricod_encode_flag( qcoder, &( qcoder->cx_blocksize[0] ), value );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_subblocksize( qcoder_env_t* qcoder, int value )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    //aricod_encode_fixed_bits(qcoder, &(qcoder->cx_subblocksize[0]), NUM_SUBBLOCKSIZE_CX_QCODER, 1, value);//xuyan0930
    aricod_encode_flag( qcoder, &( qcoder->cx_subblocksize[0] ), value );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_pred_direction( qcoder_env_t* qcoder, int direction )
{
    //NOTE: the direction is not the code in the stream, actually (direction+1)%4 is written
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_fixed_bits( qcoder, &( qcoder->cx_pred_direction[0] ), NUM_PRED_DIRECTION_CX_QCODER, 2, ( direction+1 )%4 );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_luma_8x8_coeff( qcoder_env_t* qcoder, int idx, int value, int isintra )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]

    if( isintra )
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_luma_8x8[idx<( NUM_COEFF_B8_CX_QCODER_R_INTRA-1 )  ? idx : ( NUM_COEFF_B8_CX_QCODER_R_INTRA-1 )][0] ), NUM_COEFF_B8_CX_QCODER_C_INTRA, 0, value );
    else
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_luma_8x8[idx<( NUM_COEFF_B8_CX_QCODER_R_INTER-1 )  ? idx : ( NUM_COEFF_B8_CX_QCODER_R_INTER-1 )][0] ), NUM_COEFF_B8_CX_QCODER_C_INTER, 0, value );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

//chroma only uses 8x8 block size and only 8x8 transform is performed
int qcoder_encode_chroma_8x8_coeff( qcoder_env_t* qcoder, int idx, int value, int isintra )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    if( isintra )
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_chroma_8x8[idx<( NUM_COEFF_B8_CX_QCODER_R_INTRA_CHROMA-1 ) ? idx : ( NUM_COEFF_B8_CX_QCODER_R_INTRA_CHROMA-1 )][0] ), NUM_COEFF_B8_CX_QCODER_C_INTRA_CHROMA, 0, value );
    else
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_chroma_8x8[idx<( NUM_COEFF_B8_CX_QCODER_R_INTER_CHROMA-1 ) ? idx : ( NUM_COEFF_B8_CX_QCODER_R_INTER_CHROMA-1 )][0] ), NUM_COEFF_B8_CX_QCODER_C_INTER_CHROMA, 0, value );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_luma_4x4_coeff( qcoder_env_t* qcoder, int idx, int value, int isintra )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    if( isintra )
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_luma_4x4[idx<( NUM_COEFF_B4_CX_QCODER_R_INTRA-1 ) ? idx : ( NUM_COEFF_B4_CX_QCODER_R_INTRA-1 )][0] ), NUM_COEFF_B4_CX_QCODER_C_INTRA, 0, value );
    else
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_luma_4x4[idx<( NUM_COEFF_B4_CX_QCODER_R_INTER-1 ) ? idx : ( NUM_COEFF_B4_CX_QCODER_R_INTER-1 )][0] ), NUM_COEFF_B4_CX_QCODER_C_INTER, 0, value );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_luma_16x16_coeff( qcoder_env_t* qcoder, int idx, int value, int isintra )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    if( isintra )
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_luma_16x16[idx<( NUM_COEFF_B16_CX_QCODER_R_INTRA-1 ) ? idx : ( NUM_COEFF_B16_CX_QCODER_R_INTRA-1 )][0] ), NUM_COEFF_B16_CX_QCODER_C_INTRA, 0, value );
    else
        aricod_encode_signed_expGolomb( qcoder, &( qcoder->cx_luma_16x16[idx<( NUM_COEFF_B16_CX_QCODER_R_INTER-1 ) ? idx : ( NUM_COEFF_B16_CX_QCODER_R_INTER-1 )][0] ), NUM_COEFF_B16_CX_QCODER_C_INTER, 0, value );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_transform_s4_flag( qcoder_env_t* qcoder, int s4flag )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]
    aricod_encode_flag( qcoder, &( qcoder->cx_transform_s4_flag ), s4flag );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

int qcoder_encode_transform_s16_flag( qcoder_env_t* qcoder, int s16flag )
{
    qcoder->nbits = 0;  //bit counter, only for rdo and stats [9/22/2011 Will]

    aricod_encode_flag( qcoder, &( qcoder->cx_transform_s16_flag ), s16flag );

    return qcoder->nbits;  //bit counter, only for rdo and stats [9/22/2011 Will]
}

void aricod_encode_fixed_bits( qcoder_env_t* qcoder, prob_context_t* contexts, int num_contexts, int nBits, int value )
{
    int n = 0, i;
    prob_context_t* contextp;
    for ( i = nBits - 1; i >= 0; i-- )
    {
        contextp = select_context( qcoder, n++, contexts, num_contexts );
        qcoder_encode_symbol( qcoder, contextp, ( ( value & ( 1 << i ) )!= 0 ) );
    }
}
